;; moves-plot.el -- plotting Movescout KML files in gnuplot
;;
;; Author: Carl Olsen
;;
;; Copyright (c) 2013, carl-olsen
;; All rights reserved.

;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;;
;;     * Redistributions of source code must retain the above
;;       copyright notice, this list of conditions and the following
;;       disclaimer.

;;     * Redistributions in binary form must reproduce the above
;;       copyright notice, this list of conditions and the following
;;       disclaimer in the documentation and/or other materials
;;       provided with the distribution.

;;     * Neither the name of the CoCode nor the names of its
;;       contributors may be used to endorse or promote products
;;       derived from this software without specific prior written
;;       permission.


;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
;; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
;; COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
;; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
;; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
;; OF THE POSSIBILITY OF SUCH DAMAGE.


;;------------------------------
;; use M-x moves-plot and provide the KML file from suunto movescount
;; Speed, Altitude, Pulse is used....
;; Make sure you have gnuplot in your path.
;; Also check the Move.gp for your own personal gnuplot settings 
;;------------------------------

(defun moves-plot(file)
  (interactive "fKml file:")
  ;;(when (file-readable-p file)
  (let* ( 
	 (buffName (create-copy-buffer-name file) )
	 (buffOut (concat buffName ".dat"))
	 (myBuf (generate-new-buffer buffName ) )
	 (wrtBuf (generate-new-buffer buffOut))

	 )
    (message "Creating new buffer %s" myBuf)
    (set-buffer myBuf)
    (insert-file-contents file)
    ;;Create a new window right of this one..
    ;; and select it..
    (select-window (split-window nil nil 'right))
    ;; Switch the buffer to use the thing we read in.
    (switch-to-buffer myBuf)
    ;; split the window vertically
    (select-window (split-window-vertically))
    ;; select it and use the wrtBuf on that one
    (switch-to-buffer wrtBuf)
    ;; we need to get back to the original KML buffer
    (select-window (get-buffer-window myBuf))

    (get-hr-value wrtBuf)
    (get-distance-value wrtBuf)
    (get-altitude-values wrtBuf)
    (get-speed-values wrtBuf)
    (write-buffer-to-file wrtBuf buffOut)
    (shell-command (create-gnuplot-cmd "Move.gp" buffOut ))
    )
  )

;;------------------------------
;;Common functions
;;------------------------------


(defun create-gnuplot-cmd(cfgFile fileName)
  "Creates the output cmd for gnuplot"
  (format "gnuplot -p -e \"file='%s'\" %s" fileName cfgFile)
  )
      
(defun write-buffer-to-file(wrtBuf file)
  "Writes the buffer to a file"
    (save-current-buffer
      (set-buffer wrtBuf)
      (write-file file)
      )
    )
      
  

(defun create-copy-buffer-name (file)
  "removing the .* and adding .dat on a file name"
  ( let (fName noKml) 
    (setq fName (file-name-nondirectory file))
    (setq noKml (replace-regexp-in-string ".kml" "" fName))
    noKml
    ))
    



(defun move-to-SimpleArray-end-pos()
  (let (endPt) 
    (setq endPt (search-forward "SimpleArrayData"))
    (forward-line -1)
    (end-of-line)
    (point)
    ))

  

(defun move-to-tag-start-pos (tag)
  "Move to heartrate position"
  (let (startPt) 
    (setq startPt (search-forward tag))
    (forward-line 1)
    (beginning-of-line)
    (point)
    ))

(defun make-narrow-region(tag)
  "Makes a region narrow and usint the simpleArray end position"
  (goto-char (point-min))
  (let* ( (startPt (move-to-tag-start-pos tag) )
	  (endPt (move-to-SimpleArray-end-pos) )
	  )
    (narrow-to-region startPt endPt)
    (goto-char (point-min))
))


(defun insert-val-to-column(wrtBuf val)
  "insert Hr value to buffer"
  (save-current-buffer
    (set-buffer wrtBuf)
    (end-of-line)
    (insert (format "\t%10.3f" (string-to-number val)))
    (forward-line)
    (beginning-of-line)
    ))

(defun restore-point-to-min(wrtBuf)
  " Helps restoring the point to 0 position"
  (save-current-buffer
    (set-buffer wrtBuf)
    (goto-char (point-min))
    ))

(defun get-values(wrtBuf tag iter-function)
  "Support function to get all the values from a tag"
  (goto-char (point-min))
  (make-narrow-region tag)
  (funcall iter-function wrtBuf)
  (widen)
  )

(defun iterate-values(wrtBuf valRegExp valManipFunc)
  "Iterates over some specific value range"
  (let (val)
    (while 
	(re-search-forward valRegExp nil t)
      (setq val (match-string 0))
      (when valManipFunc (setq val (funcall valManipFunc val))) 
      (insert-val-to-column wrtBuf val)
      )
    (restore-point-to-min wrtBuf)
    ))

;;------------------------------
;; Heart rate functions
;;------------------------------
(defun get-hr-value(wrtBuff)
  "Start from the beggining
   Narrow the region between heartrate positions"
  (get-values wrtBuf "Heartrate" (function iterate-hr-values))
  )
  


(defun iterate-hr-values(wrtBuf)
  "Iterates over the entire hr values"
  (let (hrVal nr)
    (setq nr 0)
    (while 
   	(re-search-forward "\\([0-9]+\.[0-9]+\\|[0-9]+\\)" nil t )		
      (setq hrVal (match-string 0))
      (setq nr (+ nr 1))
      (insert-hr-val-buf wrtBuf hrVal nr)
      )
    
    (restore-point-to-min wrtBuf)
    
    ))

    
  
(defun insert-hr-val-buf(wrtBuf hrVal nr)
  "insert Hr value to buffer"
  (save-current-buffer
    (set-buffer wrtBuf)
    (end-of-line)
    (insert (format "%s\t%.3f\n" nr (string-to-number hrVal) ) )
    ))

;;------------------------------
;;Distance function
;;------------------------------

(defun get-distance-value(wrtBuf) 
  "get the distance values"
  (get-values wrtBuf "Distance" (function iterate-dist-values))
  )
  
(defun iterate-dist-values(wrtBuf)
  "iterates over the distance values and add it to the wrtBuf"
  (iterate-values wrtBuf "\\([0-9]+\.[0-9]+\\|[0-9]+\\)" nil)
)
  
    
;;------------------------------
;;altitude functions
;;------------------------------
(defun get-altitude-values(wrtBuf)
   "get the altitude values, using the same iteration function"
   (get-values wrtBuf "Altitude" (function iterate-dist-values))
   )

;;------------------------------
;;  Speed functions
;;------------------------------
(defun get-speed-values(wrtBuf)
  "Get speed values"
  (get-values wrtBuf "Speed" (function iterate-speed-convert))
  )

  
(defun iterate-speed-convert(wrtBuf)
  "Iterates and converts the speed to pace"
  (iterate-values wrtBuf "\\([0-9]+\.[0-9]+\\|[0-9]+\\)" (function speed-conv-pace))
  )

(defun speed-conv-pace(speedStr)
  "converts speed to pace, though need to convert it to a number first"
  (let (pace)
    (setq pace (/ 60 (* 3.6 (string-to-number speedStr))))
    (if (> pace 10)
	(setq pace "NaN")
      (setq pace (number-to-string pace))
      )
    
    pace
    ))
    

  